//
//  TableViewComponent.swift
//  BaseViewControllerKit
//
//  Created by TSYH on 2024/5/14.
//

import UIKit

public class TableViewComponent: NSObject {
    public var view: UITableView {
        return tableView
    }
    
    /// 行点击事件
    public var rowDidSelectedHandler: ((Any?, IndexPath) -> Void)?
    /// cell内部按钮事件
    public var rowInnerActionHandler: ((Any?, IndexPath) -> Void)?
    
    public var rowWillDisplayHandler: ((UITableViewCell, Any?, IndexPath) -> Void)?
    public var rowDidEndDisplayingHandler: ((UITableViewCell, Any?, IndexPath) -> Void)?
    
    public var didScrollHandler: ((UIScrollView) -> Void)?
    public var willBeginDraggingHandler: ((UIScrollView) -> Void)?
    public var didEndDraggingHandler: ((UIScrollView, Bool) -> Void)?
    public var didScrollToTopHandler: ((UIScrollView) -> Void)?
    public var willEndDraggingWithVelocityHandler: ((UIScrollView, CGPoint) -> Void)?
    public var didEndDeceleratingHandler: ((UIScrollView) -> Void)?
    
    /// 编辑菜单点击事件
    public var rowEditSelectedHandler: ((TableViewRowAction, Any?, IndexPath) -> Void)?
    
    private var tableView: UITableView!
    public init(frame: CGRect, attributes: [String: Any]? = nil) {
        super.init()
        self.attributes = attributes
        
        var style = UITableView.Style.plain
        if let shoulPin = attributes?["HeaderShouldHovering"] as? Bool {
            style = shoulPin ? .plain : .grouped
        }
        let tb = UITableView(frame: .zero, style: style)
        tb.separatorStyle = .none
        tb.dataSource = self
        tb.delegate = self
        tb.frame = frame
        tableView = tb
    }
    
    lazy var dataSource = [TableViewSectionComponent]()
    var attributes: [String: Any]?
    
    public func reloadData() {
        tableView.reloadData()
    }
    
    public func clear() {
        dataSource.removeAll()
    }
    
    public func append(sections: [TableViewSectionComponent]) {
        dataSource.append(contentsOf: sections)
    }
    
    // 添加子元素
    public func append(childrens: [Component]) {
        var lastSection = dataSource.last
        if lastSection == nil {
            lastSection = DefaultTableViewSectionComponent(cells: [])
        }
        childrens.forEach { child in
            lastSection?.append(children: child)
        }
        if dataSource.isEmpty, let lastSection = lastSection {
            dataSource.append(lastSection)
        }
    }
}

extension TableViewComponent: UITableViewDataSource, UITableViewDelegate {
    func component(at indexPath: IndexPath) -> TableViewCellComponent? {
        return dataSource.value(at: indexPath.section)?.cellComponents.value(at: indexPath.row)
    }
    
    public func numberOfSections(in tableView: UITableView) -> Int {
        return dataSource.count
    }
    
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataSource.value(at: section)?.cellComponents.count ?? 0
    }
    
    // MARK: Cell
    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return component(at: indexPath)?.cellHeight(with: attributes) ?? 0
    }
    
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let item = component(at: indexPath) else {
            return UITableViewCell()
        }
        var cell = tableView.dequeueReusableCell(withIdentifier: item.tableCellClass.description())
        if cell == nil {
            cell = item.tableCellClass.init(style: .default, reuseIdentifier: item.tableCellClass.description())
        }
        if let cell = cell as? TableComponentView {
            cell.renderView(with: item, component: self, attributes: attributes)
        }
        cell?.selectionStyle = .none
        return cell!
    }
    
    // MARK: Header
    public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return dataSource.value(at: section)?.headerComponent?.viewHeight ?? 0
    }
    
    public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        guard let header = dataSource.value(at: section)?.headerComponent else {
            return nil
        }
        var view = tableView.dequeueReusableHeaderFooterView(withIdentifier: header.viewClass.description())
        if view == nil {
            view = header.viewClass.init(reuseIdentifier: header.viewClass.description())
        }
        if let view = view as? TableComponentView {
            view.renderView(with: header, component: self, attributes: attributes)
        }
        return view
    }
    
    // MARK: Footer
    public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        return dataSource.value(at: section)?.footerComponent?.viewHeight ?? 0
    }
    
    public func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        guard let footer = dataSource.value(at: section)?.footerComponent else {
            return nil
        }
        var view = tableView.dequeueReusableHeaderFooterView(withIdentifier: footer.viewClass.description())
        if view == nil {
            view = footer.viewClass.init(reuseIdentifier: footer.viewClass.description())
        }
        if let view = view as? TableComponentView {
            view.renderView(with: footer, component: self, attributes: attributes)
        }
        return view
    }
    
    public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        rowWillDisplayHandler?(cell, component(at: indexPath), indexPath)
    }
    
    public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        rowDidEndDisplayingHandler?(cell, component(at: indexPath), indexPath)
    }
    
    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        rowDidSelectedHandler?(component(at: indexPath), indexPath)
    }
    
    // MARK: Edit
    public func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        guard rowEditSelectedHandler != nil else {
            return false
        }
        if let actions = component(at: indexPath)?.editActions, !actions.isEmpty {
            return true
        }
        return false
    }
    
    public func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        if let actions = component(at: indexPath)?.editActions, !actions.isEmpty {
            let values = actions.map { item in
                let action = UIContextualAction(style: .normal, title: item.title) { [weak self] action, view, handler in
                    self?.rowEditSelectedHandler?(item, self?.component(at: indexPath), indexPath)
                }
                action.backgroundColor = item.backgroundColor
                action.image = item.image
                return action
            }
            return UISwipeActionsConfiguration(actions: values)
        }
        return nil
    }
}

extension TableViewComponent {
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        didScrollHandler?(scrollView)
    }
    
    public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        willEndDraggingWithVelocityHandler?(scrollView, velocity)
    }
    
    public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        willBeginDraggingHandler?(scrollView)
    }
    
    public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        didEndDraggingHandler?(scrollView, decelerate)
    }
    
    public func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
        didScrollToTopHandler?(scrollView)
    }
    
    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        didEndDeceleratingHandler?(scrollView)
    }
}
